package com.ddxz.middleground.master.service.impl;

import com.ddxz.middleground.master.dto.MasterHeader;
import com.ddxz.middleground.master.dto.menu.Menu;
import com.ddxz.middleground.master.dto.menu.MenuDetail;
import com.ddxz.middleground.master.dto.menu.MenuInfo;
import com.ddxz.middleground.master.entity.MenuEntity;
import com.ddxz.middleground.master.entity.MenuPermissionEntity;
import com.ddxz.middleground.master.mapper.MenuMapper;
import com.ddxz.middleground.master.mapper.MenuPermissionMapper;
import com.ddxz.middleground.master.mapper.RolePermissionMapper;
import com.ddxz.middleground.master.service.MenuService;
import com.ddxz.tool.core.common.CommonUtil;
import com.ddxz.tool.core.common.DdxzTree;
import com.ddxz.tool.core.common.DdxzTreeUtil;
import com.ddxz.tool.core.convert.BeanUtil;
import com.ddxz.tool.core.convert.CollectionUtil;
import com.ddxz.tool.core.convert.DdxzUtil;
import com.ddxz.tool.core.convert.StringUtil;
import com.ddxz.tool.web.dto.DdxzException;
import com.ddxz.tool.web.dto.DdxzResponse;
import org.springframework.stereotype.Service;

import java.util.*;

import static com.ddxz.middleground.master.util.ErrorStatus.*;

/**
 * class description
 *
 * @author 朱宗刚
 * @version 1.0.0
 * @date 2020/7/30
 */

@Service
public class MenuServiceImpl implements MenuService, DdxzTreeUtil {
    @Override
    public DdxzResponse getAppMenuTree(MasterHeader header, String appId, String keyword, String permissionId) {
        List<DdxzTree> tree = new ArrayList<>();
        List<Menu> tmps;
        List<MenuEntity> menus;
        List<String> menuIds = new LinkedList<>();
        menuIds.add("-1");

        while (!CollectionUtil.isEmpty(menuIds)) {
            menus = menuMapper.getMenuListByParent(appId, menuIds.get(0), header.getTenant());
            if (CollectionUtil.isEmpty(menus)) {
                menuIds.remove(0);
                continue;
            }
            menus.forEach(item -> menuIds.add(item.getId()));
            tmps = new ArrayList<>(menus.size());
            CollectionUtil.copyCollection(menus, tmps, Menu.class, DdxzUtil.SECOND_DATE);
            if (CollectionUtil.isEmpty(tree)) {
                tree.addAll(tmps);
            } else {
                DdxzTree tmp = findItem(tree, menuIds.get(0));
                tmp.getChildren().addAll(tmps);
            }

            menuIds.remove(0);
        }

        if (StringUtil.isEmpty(permissionId)) {
            setMenuStatus(tree, permissionId, header.getTenant());
        }

        if (StringUtil.isEmpty(keyword)) {
            return DdxzResponse.correct(tree);
        } else {
            return DdxzResponse.correct(treeFilter(tree, keyword));
        }
    }

    @Override
    public DdxzResponse menuSave(MasterHeader header, MenuInfo menuInfo) {
        Integer num = menuMapper.getNumByName(menuInfo.getName(), menuInfo.getParentId(), header.getTenant());
        CommonUtil.ddxzAssetGreat(num, 0, MENU_NAME_REPEAT);
        num = menuMapper.getNumByCode(menuInfo.getCode(), menuInfo.getParentId(), header.getTenant());
        CommonUtil.ddxzAssetGreat(num, 0, MENU_CODE_REPEAT);
        MenuEntity entity = new MenuEntity();
        BeanUtil.copyProperties(menuInfo, entity);
        if (StringUtil.isEmpty(menuInfo.getId())) {
            // 新增菜单
            entity.setId(CommonUtil.generatorId(T_MENU));
            entity.setUserId(header.getUserId());
            entity.setNickname(header.getNickname());
            menuMapper.saveMenu(entity, header.getTenant());
        } else {
            // 修改已有菜单
            menuMapper.modifyMenu(entity, header.getTenant());
        }

        return DdxzResponse.correct("保存菜单成功", entity.getId());
    }

    @Override
    public DdxzResponse menuDetail(MasterHeader header, String menuId) {
        MenuEntity entity = menuMapper.getMenuDetail(menuId, header.getTenant());
        CommonUtil.ddxzAssetNull(entity, MENU_NOT_EXISTS);
        return DdxzResponse.correct(BeanUtil.getTarget(entity, MenuDetail.class, DdxzUtil.SECOND_DATETIME));
    }

    @Override
    public DdxzResponse menuRemove(MasterHeader header, String menuId) {
        int num = menuMapper.getNumByMenuId(menuId, header.getTenant());
        CommonUtil.ddxzAssetGreat(num, 0, MENU_HAS_CHILDREN);
        num = permissionMapper.getNumByMenuId(menuId, header.getTenant());
        CommonUtil.ddxzAssetGreat(num, 0, MENU_IN_USE);
        menuMapper.removeMenu(menuId, header.getTenant());
        return DdxzResponse.correct("菜单删除成功", OK_MESSAGE);
    }

    @Override
    public DdxzResponse menuMove(MasterHeader header, Integer direction, String menuId) {
        MenuEntity entity = menuMapper.getMenuDetail(menuId, header.getTenant());
        CommonUtil.ddxzAssetNull(entity, MENU_NOT_EXISTS);
        switch (direction) {
            case UP:
                return moveUp(entity, header.getTenant());
            case DOWN:
                return moveDown(entity, header.getTenant());
            case INCREASE:
                return moveIncrease(entity, header.getTenant());
            case DECREASE:
                return moveDecrease(entity, header.getTenant());
            default:
                throw new DdxzException(INVALID_DIRECTION);
        }
    }

    @Override
    public DdxzResponse getAppMenuTree(MasterHeader header, String appId) {
        if (StringUtil.isEmpty(header.getRoleIds())) {
            return DdxzResponse.error(NO_ROLE_BIND);
        }

        List<String> permList = roleMapper.getPermissionList(StringUtil.getList(header.getRoleIds()), header.getTenant());
        if (CollectionUtil.isEmpty(permList)) {
            return DdxzResponse.error(NO_PERMISSION_BIND);
        }

        List<DdxzTree> menuTree = new ArrayList<>();
        List<Menu> tmps;
        List<MenuEntity> menus;
        List<String> menuIds = new LinkedList<>();
        menuIds.add("-1");

        while (!CollectionUtil.isEmpty(menuIds)) {
            menus = menuMapper.getMenuListByParentAndPermissions(appId, menuIds.get(0), header.getTenant(), permList);
            if (CollectionUtil.isEmpty(menus)) {
                menuIds.remove(0);
                continue;
            }
            menus.forEach(item -> menuIds.add(item.getId()));
            tmps = new ArrayList<>(menus.size());
            CollectionUtil.copyCollection(menus, tmps, Menu.class, DdxzUtil.SECOND_DATE);
            if (CollectionUtil.isEmpty(menuTree)) {
                menuTree.addAll(tmps);
            } else {
                DdxzTree tmp = findItem(menuTree, menuIds.get(0));
                tmp.getChildren().addAll(tmps);
            }

            menuIds.remove(0);
        }
        return DdxzResponse.correct(menuTree);
    }

    private void setMenuStatus(List<DdxzTree> tree, String permissionId, String tenant) {
        List<MenuPermissionEntity> entityList = permissionMapper.getMenuIdsByPermissionId(permissionId, tenant);
        Map<String, Integer> nodeState = convertListToMap(entityList);
        List<DdxzTree> tmp = new LinkedList<>(tree);
        DdxzTree node;
        while (!CollectionUtil.isEmpty(tmp)) {
            node = tmp.get(0);
            node.setState(nodeState.getOrDefault(node.getId(), DdxzTree.NONE_SELECTED));
            if(!CollectionUtil.isEmpty(node.getChildren())) {
                tmp.addAll(node.getChildren());
            }

            tmp.remove(0);
        }
    }

    private Map<String, Integer> convertListToMap(List<MenuPermissionEntity> entityList) {
        Map<String, Integer> nodeState = new HashMap<>(entityList.size());
        entityList.forEach(entity -> nodeState.put(entity.getMenuId(), entity.getStatus()));
        return nodeState;
    }

    private DdxzResponse moveUp(MenuEntity entity, String tenant) {
        CommonUtil.ddxzAssetEqual(entity.getOrder(), CANNOT_UP_ORDER, CANNOT_UP);
        menuMapper.updateOrderByOrder(entity.getParentId(), entity.getOrder() - 1, entity.getOrder(), tenant);
        menuMapper.updateOrderById(entity, entity.getOrder() - 1, tenant);
        return DdxzResponse.correct("菜单上移成功", entity.getId());
    }

    private DdxzResponse moveDown(MenuEntity entity, String tenant) {
        int num = menuMapper.getNumByMenuId(entity.getParentId(), tenant);
        CommonUtil.ddxzAssetUnless(entity.getOrder(), num, CANNOT_DOWN);
        menuMapper.updateOrderByOrder(entity.getParentId(), entity.getOrder() + 1, entity.getOrder(), tenant);
        menuMapper.updateOrderById(entity, entity.getOrder() + 1, tenant);
        return DdxzResponse.correct("菜单下移成功", entity.getId());
    }

    private DdxzResponse moveIncrease(MenuEntity entity, String tenant) {
        CommonUtil.ddxzAssetEqual(entity.getParentId(), TOP_NODE_PARENT_ID, CANNOT_INC);
        CommonUtil.ddxzAssetUnequal(entity.getType(), MENU, CANNOT_INC);
        String ppid = menuMapper.getParentId(entity.getParentId(), tenant);
        Integer num = menuMapper.getNumByMenuId(ppid, tenant);
        menuMapper.updateBatchOrder(entity.getParentId(), entity.getOrder(), tenant);
        entity.setOrder(num + 1);
        entity.setParentId(ppid);
        menuMapper.updateOrderById(entity, entity.getOrder(), tenant);
        return DdxzResponse.correct("菜单升级成功", entity.getId());
    }

    private DdxzResponse moveDecrease(MenuEntity entity, String tenant) {
        CommonUtil.ddxzAssetLess(entity.getOrder(), MIN_DECREASE_ORDER, CANNOT_DEC);
        String npid = menuMapper.getIdByOrder(entity.getParentId(), entity.getOrder() - 1, tenant);
        Integer num = menuMapper.getNumByMenuId(npid, tenant);
        menuMapper.updateBatchOrder(entity.getParentId(), entity.getOrder(), tenant);
        entity.setParentId(npid);
        entity.setOrder(num + 1);
        menuMapper.updateOrderById(entity, entity.getOrder(), tenant);
        return DdxzResponse.correct("菜单降级成功", entity.getId());
    }

    public MenuServiceImpl(MenuMapper menuMapper, MenuPermissionMapper permissionMapper, RolePermissionMapper roleMapper) {
        this.menuMapper = menuMapper;
        this.permissionMapper = permissionMapper;
        this.roleMapper = roleMapper;
    }

    private final MenuMapper menuMapper;
    private final MenuPermissionMapper permissionMapper;
    private final RolePermissionMapper roleMapper;
}
